package cpe.hapa;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Properties;
import java.util.UUID;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.json.simple.JSONObject;

import com.google.appengine.api.datastore.DatastoreService;
import com.google.appengine.api.datastore.DatastoreServiceFactory;
import com.google.appengine.api.datastore.EntityNotFoundException;
import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.KeyFactory;
import com.google.appengine.api.taskqueue.Queue;
import com.google.appengine.api.taskqueue.QueueFactory;
import com.google.appengine.api.taskqueue.TaskOptions;
import com.google.appengine.api.taskqueue.TaskOptions.Method;
import com.google.appengine.labs.repackaged.org.json.JSONArray;

import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.AddressException;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;

import cpe.hapa.authentication.Authenticate;
import cpe.hapa.authentication.SessionHandler;
import cpe.hapa.dao.RoleDAO;
import cpe.hapa.dao.UserDAO;
import cpe.hapa.model.User;

/**
 * Servlet implementation class InscriptionServlet
 * TODO CRON QUI CHECK LES SESSIONS
 */
public class UserServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;
       
    /**
     * @see HttpServlet#HttpServlet()
     */
    public UserServlet() {
        super();
    }

	/**
	 * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
	 */
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		switch(request.getParameter("cmd")) {
			case "listeUser":
				listUser(request,response);
				break;
			case "userDetails":
				userDetails(request,response);
				break;
			case "authenticationInfos":
				authenticationInfos(request,response);
				break;
			case "checkAllSessions":
				try {
					SessionHandler.checkAllSessions();
				} catch (NumberFormatException | ParseException | EntityNotFoundException e) {
					throw new ServletException(e);
				}
				break;
			default:
				throw new ServletException("Cette commande n'existe pas");
		}
	}

	/**
	 * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
	 */
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		switch(request.getParameter("cmd")) {
			case "register" :
				register(request, response);
				response.getWriter().print("{\"success\":\"Utilisateur ajouté\"}");
				break;
			case "addUser" :
				adminRegister(request, response);
				break;
			case "addUserQueue" :
				addUserQueue(request, response);
				response.getWriter().print("{\"redirect\":\"userAdministration.html\"}");
				break;
			case "login":
				// voir les redirections dans la méthode login
				login(request, response);
				break;
			case "changePassword":
				changePassword(request, response);
				break;
			case "disconnect":
				try {
					Authenticate.disconnect(request);
				} catch (NumberFormatException | ParseException | EntityNotFoundException e) {
					throw new ServletException("La déconnexion a échoué", e);
				}
				response.getWriter().print("{\"redirect\":\"index.html\"}");
				break;
			default:
				throw new ServletException("Cette commande n'existe pas");
		}
	}

	@Override
	protected void doDelete(HttpServletRequest req, HttpServletResponse resp) throws ServletException
	{
		switch(req.getParameter("cmd")) {
			case "deleteUser" :
				DatastoreService datastore = DatastoreServiceFactory.getDatastoreService();
				String test = req.getParameter("id");
				Key deleteKey = KeyFactory.stringToKey(test);
				datastore.delete(deleteKey);
				break;
			case "deleteUserQueue" :
				Queue queue = QueueFactory.getDefaultQueue();
				TaskOptions task = TaskOptions.Builder.withUrl("/user?cmd=deleteUser&id=" + req.getParameter("id")).method(Method.DELETE);
				queue.add(task);
				break;
			default:
				throw new ServletException("Cette commande n'existe pas");
		}
	}
	
	
	/* *******************************
	 * =============================
	 * COMMANDES ET SOUS-METHODES
	 * =============================
	 * ********************************/
	
	private void changePassword(HttpServletRequest request,HttpServletResponse response) throws ServletException, IOException {
		User user = null;
		try {
			user = Authenticate.getConnectedUser(request);
		} catch(Exception e) {
			throw new ServletException(e);
		}
		
		if(user == null) {
			response.getWriter().print("{\"redirect\":\"index.html\"}");
		} else {
			String oldPassword = request.getParameter("oldPassword");
			String newPassword = request.getParameter("newPassword");
			String newPassword2 = request.getParameter("newPassword2");
			
			if(user.getPassword().equals(oldPassword)) {
				if(newPassword != null && !newPassword.isEmpty() && newPassword.equals(newPassword2)) {
					try {
						user.setPassword(newPassword);
						user.setIsPasswordChanged(true);
						UserDAO.update(user);
					} catch (Exception e) {
						throw new ServletException(e);
					}
					
					response.getWriter().print("{\"redirect\":\"home.html\"}");
				} else {
					response.sendError(HttpServletResponse.SC_BAD_REQUEST,"Pas de nouveau mot de passe ou la confirmation du nouveau mot de passe n'est pas correcte");
				}
			} else {
				response.sendError(HttpServletResponse.SC_BAD_REQUEST,"Le mot de passe actuel ne correspond pas");
			}
		}
	}
	
	private void authenticationInfos(HttpServletRequest request,HttpServletResponse response) throws IOException, ServletException {
		Boolean connected = false;
		String authInfos = "";
		try {
			User user = Authenticate.getConnectedUser(request);
			if(user != null) {
				connected = true;
				String redirect = null;
				if(user.isPasswordChanged()) {
					redirect = "userDetails.html";
				} else {
					redirect = "changePassword.html";
				}
				authInfos = ","
					+ "\"redirect\":\"" + redirect + "\","
					+ "\"duration\":" + (int) (user.getDureeConnexion()/60) + ","
					+ "\"role\":\"" + user.getRole() + "\","
					+ "\"username\":\"" + user.getPrenom() + " " + user.getNom() + "\","
					+ "\"lastConnection\":\"" + new SimpleDateFormat("dd/MM/yyyy hh:mm").format(user.getDateConnexion()) + "\"";
			}
			response.getWriter().print("{"+
				"\"connected\":" + connected + ","
				+ "\"nbUserConnected\":" + SessionHandler.getTotalActiveSession()
				+ authInfos + "}"
			);
		} catch(Exception e) {
			throw new ServletException(e);
		}
	}
	
	protected void register(HttpServletRequest request, HttpServletResponse response) throws ServletException,IOException {
		// TODO : vérifier que login est unique
		String nom = request.getParameter("nom");
		String prenom = request.getParameter("prenom");
		String dateNaissance = request.getParameter("dateNaissance");
		String email = request.getParameter("email");
		String login = request.getParameter("login");
		String role = RoleDAO.getUserRole();
		String isPasswordChanged = "false";
		String creationDate = new SimpleDateFormat("yyyy-MM-dd").format(new Date());
		User user = null;
		String password = UUID.randomUUID().toString().substring(0, 9);
		
		try {
			if(UserDAO.count()==0) {
				role = RoleDAO.getAdminRole();
			}
			user = this.checkAndInstantiateUserModel(nom, prenom, dateNaissance, email, login, password, role, isPasswordChanged, null, null, creationDate);
		} catch(ParseException e) {
			throw new ServletException(e);
		}
		
		if (user != null) {
			UserDAO.addUser(user);
			sendMailForAuth(user);
		} else {
			throw new ServletException("Données du formulaire incorrectes");
		}
	}
	
	private void adminRegister(HttpServletRequest request,HttpServletResponse response) throws ServletException, IOException { 
		// TODO : vérifier que login est unique
		String nom = request.getParameter("nom");
		String prenom = request.getParameter("prenom");
		String dateNaissance = request.getParameter("dateNaissance");
		String email = request.getParameter("email");
		String login = request.getParameter("login");
		String role = request.getParameter("role");
		String isPasswordChanged = "false";
		String creationDate = new SimpleDateFormat("yyyy-MM-dd").format(new Date());
		User user = null;
		String password = request.getParameter("password");
		String rePassword = request.getParameter("password2");
		
		if(password == null || !password.equals(rePassword)) {
			throw new ServletException("Les mots de passe ne sont pas corrects");
		}
		
		
		try {
			user = this.checkAndInstantiateUserModel(nom, prenom, dateNaissance, email, login, password, role, isPasswordChanged, null, null, creationDate);
		} catch (ParseException e) {
			response.sendError(HttpServletResponse.SC_BAD_REQUEST,e.getMessage());
		}

		
		if (user != null) {
			UserDAO.addUser(user);
			sendMailForAuth(user);
		} else {
			response.sendError(HttpServletResponse.SC_BAD_REQUEST,"Données du formulaire incorrectes");
		}
	}

	private void addUserQueue(HttpServletRequest request,HttpServletResponse response) throws ServletException, IOException {
		User connectedUser = null;
		
		try {
			connectedUser = Authenticate.getConnectedUser(request);
		} catch (NumberFormatException | ParseException e) {
			throw new ServletException(e);
		}
		
		if( !(connectedUser != null && connectedUser.isInRole(RoleDAO.getAdminRole())) ) {
			throw new ServletException("Vous n'êtes pas administrateur, vous ne pouvez pas ajouter d'utilisateur");
		}
		
		String nom = request.getParameter("nom");
		String prenom = request.getParameter("prenom");
		String dateNaissance = request.getParameter("dateNaissance");
		String email = request.getParameter("email");
		String login = request.getParameter("login");
		String role = request.getParameter("role");
		String password = request.getParameter("password");
		String rePassword = request.getParameter("password2");
		try {
			checkAndInstantiateUserModel(nom, prenom, dateNaissance, email, login, password, role, "false", null, null,"01-01-2013");
			if(password == null || password.trim().isEmpty() || !password.equals(rePassword)) {
				throw new ParseException("Les mots de passe ne sont pas corrects", 0);
			}
		} catch (ParseException e) {
			response.sendError(HttpServletResponse.SC_BAD_REQUEST,e.getMessage());
		}
		
		Queue queue = QueueFactory.getDefaultQueue();
		TaskOptions task=TaskOptions.Builder.withUrl("/user")
			.param("cmd", "addUser")
			.param("nom", request.getParameter("nom"))
			.param("prenom", request.getParameter("prenom"))
			.param("dateNaissance", request.getParameter("dateNaissance"))
			.param("email", request.getParameter("email"))
			.param("login", request.getParameter("login"))
			.param("password", request.getParameter("password"))
			.param("password2", request.getParameter("password2"))
			.param("role", request.getParameter("role"));
		queue.add(task);
	}
	
	protected void login(HttpServletRequest request, HttpServletResponse response) throws ServletException {
		String login = request.getParameter("login");
		String password = request.getParameter("password");
		User user = null;
		
		try {
			if(!Authenticate.authenticate(request, login, password)) {
				response.sendError(HttpServletResponse.SC_FORBIDDEN,"Mauvais login et/ou mot de passe");
			} else {
				user = Authenticate.getConnectedUser(request);
				if(!user.isPasswordChanged()) {
					response.getWriter().print("{\"redirect\":\"changePassword.html\"}");
				} else {
					response.getWriter().print("{\"redirect\":\"userDetails.html\"}");
				}
			}
		} catch(Exception e) {
			throw new ServletException("Échec de l'authentification pour une raison inconnue : " + e.getMessage(),e);
		}
	}
	
	protected void listUser(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		
		try {
			JSONArray userList = new JSONArray();
			for(User result : UserDAO.getAll())
			{
				JSONObject user = new JSONObject();
				user.put("id", KeyFactory.keyToString(result.getKey()));
				user.put("nom", result.getNom());
				user.put("prenom", result.getPrenom());
				user.put("dateNaissance", result.getDateNaissance());
				user.put("email", result.getEmail());
				user.put("login", result.getLogin());
				user.put("role", result.getRole());
				user.put("dateCreation", result.getDateCreation());
				user.put("lastConnection", result.getDateConnexion());
				user.put("duration", result.getDureeConnexion());
				userList.put(user);
			}

			response.getWriter().print(userList);
		} catch (NumberFormatException | ParseException e) {
			e.printStackTrace();
		}
	}
	
	private void userDetails(HttpServletRequest request,HttpServletResponse response) throws ServletException, IOException {
		User user = null;
		try {
			user = Authenticate.getConnectedUser(request);
		} catch (NumberFormatException | ParseException e) {
			throw new ServletException(e);
		}
		
		if(user == null) {
			throw new ServletException("Vous n'êtes pas/plus connecté");
		}
		
		SimpleDateFormat simpleDateFormat = new SimpleDateFormat("dd/MM/yyyy");
		JSONObject jsonUser = new JSONObject();
		jsonUser.put("id", KeyFactory.keyToString(user.getKey()));
		jsonUser.put("nom", user.getNom());
		jsonUser.put("prenom", user.getPrenom());
		jsonUser.put("dateNaissance", simpleDateFormat.format(user.getDateNaissance()));
		jsonUser.put("email", user.getEmail());
		jsonUser.put("login", user.getLogin());
		jsonUser.put("dateCreation", simpleDateFormat.format(user.getDateCreation()));
		if(user.getDateConnexion() != null) {
			jsonUser.put("lastConnection", simpleDateFormat.format(user.getDateConnexion()));
		}
		jsonUser.put("duration", user.getDureeConnexion());
		
		response.getWriter().print(jsonUser);
	}

	protected User checkAndInstantiateUserModel(String nom,String prenom,String dateNaissance,String email,String login,String password,String role,String isPasswordChanged,String dateConnexion,String dureeConnexion,String dateCreation) throws ParseException, ServletException {
		User user = null;
		if ( nom != null && 
				prenom != null && 
				dateNaissance != null && 
				email != null && 
				login != null && 
				password != null && 
				role != null &&
				isPasswordChanged != null &&
				!nom.isEmpty() && 
				!prenom.isEmpty() && 
				!dateNaissance.isEmpty() && 
				!email.isEmpty() && 
				!login.isEmpty() && 
				!password.isEmpty() && 
				!role.isEmpty() &&
				!isPasswordChanged.isEmpty())
		{
			SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
			user = new User(
				null,
				nom, 
				prenom, 
				sdf.parse(dateNaissance),
				email,
				login, 
				password,
				role,
				Boolean.valueOf(isPasswordChanged),
				(dateConnexion != null ? new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ").parse(dateConnexion) : null),
				(dureeConnexion != null ? Long.valueOf(dureeConnexion) : null),
				sdf.parse(dateCreation)
			);
			
		} else {
			throw new ServletException("Données fournies incorrectes");
		}
		return user;
	}
	
	private void sendMailForAuth(User user) throws ServletException {
		Properties props = new Properties();
        Session session = Session.getDefaultInstance(props, null);

        String msgBody = "Bonjour,"
        		+ "\n\nJe te fais part de tes informations d'authentification suite à ta demande de création de compte sur VolsCPE:"
        		+ "\n\nlogin: " + user.getLogin() + ""
				+ "\nMot de passe: " + user.getPassword() + ""
				+ "\n\nA bientôt sur VolsCPE!" + ""
				+ "\nhttp://reservation-vole-cpe.appspot.com/";

        try {
            Message msg = new MimeMessage(session);
            msg.setFrom(new InternetAddress("donotreply@reservation-vole-cpe.appspotmail.com", "reservation-vole-cpe.appspotmail.com Admin"));
            msg.addRecipient(Message.RecipientType.TO, new InternetAddress(user.getEmail(), user.getPrenom() + " " + user.getNom()));
            msg.setSubject("VoyageCPE - Ton compte peut décoller!");
            msg.setText(msgBody);
            Transport.send(msg);
        } catch (AddressException e) {
        	throw new ServletException(e);
        } catch (MessagingException | UnsupportedEncodingException e) {
        	throw new ServletException(e);
        }
	}
}
